﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nature.Numerics;
using Nature.Numerics.Basic;


namespace Nature.Toolkit.GMix
{
    [DebuggerDisplay("{ComponentName}, Index: {ComponentIndex}, FractionsCount: {FractionsCount}")]
    class ComponentInfo
    {
        #region Nested Types

        struct FractionDescriptor
        {
            internal int Index { get; set; }

            internal bool IsSpecies { get; set; }

            internal bool IsComponent { get { return !IsSpecies; } }

            internal double? ExplicitValue { get; set; }

            internal int? ParameterIndex { get; set; }

            internal bool IsImplicit { get { return (ExplicitValue.HasValue == false && ParameterIndex.HasValue == false); } }

            public override string ToString()
            {
                return string.Format("{0}, {1}, Index: {2}",
                    (IsSpecies ? "Species" : "Component"),
                    (ExplicitValue.HasValue ? ExplicitValue.ToString() : (ParameterIndex.HasValue ? "Parameterized" : "[?]")),
                    Index);
            }
        }

        class ScalarDescriptor
        {
            internal int? ParameterIndex { get; set; }

            internal double? ExplicitValue { get; set; }

            /// <summary>
            /// True if the pressure is defined either explicitely or as a parameter
            /// </summary>
            internal bool IsDefined { get { return this.ExplicitValue.HasValue || this.ParameterIndex.HasValue; } }

            internal bool IsQuestion { get { return !(IsDefined); } }

        }

        delegate void FillProjectionHandler(MixProjection projection);

        #endregion

        #region Private Fields

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private FractionDescriptor[] m_fractionDescriptors;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_equivalenceRatioDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_mixtureFractionDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_temperatureDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_pressureDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_baseTemperatureDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_basePressureDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_compressionRatioDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        private ScalarDescriptor m_machNumberDescriptor;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private ComponentInfo[] m_subComponents;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string[] m_speciesNames;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        FillProjectionHandler m_calcCompositionHandler;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        FillProjectionHandler m_tpHandler;

        #endregion   

        #region Constructor

        internal ComponentInfo(IComponent component, ExpressionInfo expression)
        {
            this.SourceContract = component;
            this.Expression = expression;
            this.ComponentName = component.ComponentName.ToLower();
            this.IsRoot = component.IsRoot;
            this.DisplayName = component.DisplayName;
            this.CompositionScale = component.ActualUnits.CompositionScale;
            this.IsMassBased = component.ActualUnits.IsMassBased;
            this.TemperatureUnits = component.ActualUnits.TemperatureUnits;
            this.PressureUnits = component.ActualUnits.PressureUnits;

            InitializeFractionDescriptors();

            InitializeEquivalenceRatioDescription();

            InitializeMixtureFractionDescriptor();

            InitializeTemperatureDescriptor();

            InitializePressureDescriptor();

            InitializeBaseTemperatureDescriptor();

            InitializeBasePressureDescriptor();

            InitializeCompressionRatioDescriptor();

            InitializeMachNumberDescriptor();

            RunIntegrityCheck();

            SetupConcentrationDeterminationHandler();

            SetupThermoStateDeterminationHandler();
        }

        private void SetupThermoStateDeterminationHandler()
        {
            m_tpHandler = new FillProjectionHandler(this.BasicTpCalculator);
                
            bool implicitTpCase = (
                    (m_temperatureDescriptor != null && m_temperatureDescriptor.IsQuestion) ||
                    (m_pressureDescriptor != null && m_pressureDescriptor.IsQuestion)
                );

            bool isCompressionCase = (true == implicitTpCase && 
                m_baseTemperatureDescriptor != null && 
                m_basePressureDescriptor != null);

            bool isShockWaveCase = (m_machNumberDescriptor != null);


            bool isAdiabaticMixingCase = (false == isCompressionCase && true == implicitTpCase);

            if (isAdiabaticMixingCase)
            {
                m_tpHandler = new FillProjectionHandler(this.AdiabaticMixingTpCalculator);
            }
            else if (isCompressionCase && false == isShockWaveCase)
            {
                m_tpHandler = new FillProjectionHandler(this.IsentropicCompressionTpCalculator);
            }
            else if (isCompressionCase && isShockWaveCase)
            {
                m_tpHandler = new FillProjectionHandler(this.ShockWaveTpCalculator);
            }  
        }

        private void SetupConcentrationDeterminationHandler()
        {
            bool isFuOxFormulation = (m_equivalenceRatioDescriptor != null || m_mixtureFractionDescriptor != null);

            if (false == isFuOxFormulation)
            {
                m_calcCompositionHandler = new FillProjectionHandler(this.BasicCompositionCalculator);
            }
            else if (m_equivalenceRatioDescriptor != null)
            {
                m_calcCompositionHandler = new FillProjectionHandler(this.EquivalnceRatioCompositionCalculator);
            }
            else if (m_mixtureFractionDescriptor != null)
            {
                m_calcCompositionHandler = new FillProjectionHandler(this.MixtureFractionCompositionCalculator);
            }
            else
            {
                throw new ArgumentException();
            }
        }

        private void RunIntegrityCheck()
        {
            if (IsFuOxCase)
            {
                // either EquivalenceRatio or Mixture Fraction, but not both
                if (m_equivalenceRatioDescriptor != null && m_mixtureFractionDescriptor != null && 
                    m_equivalenceRatioDescriptor.IsDefined && m_mixtureFractionDescriptor.IsDefined)
                {
                    throw new MixBadConditionedCompositionException();
                }


                if (m_fractionDescriptors.Count() != 2 ||
                    m_fractionDescriptors.Count(i => i.IsImplicit) != 2)
                {
                    throw new MixBadConditionedCompositionException();
                }
            }
            else
            {
                if (m_fractionDescriptors.Count(i => i.IsImplicit) > 1) { throw new MixBadConditionedCompositionException(); }
            }            
        }

        private void InitializeBasePressureDescriptor()
        {
            m_basePressureDescriptor = CreateScalarDescriptor<ILhsPressureBase>();
        }

        private void InitializeCompressionRatioDescriptor()
        {
            m_compressionRatioDescriptor = CreateScalarDescriptor<ILhsCompressionRatio>();
        }

        private void InitializeMachNumberDescriptor()
        {
            m_machNumberDescriptor = CreateScalarDescriptor<ILhsMachNumber>();
        }

        private void InitializeBaseTemperatureDescriptor()
        {
            m_baseTemperatureDescriptor = CreateScalarDescriptor<ILhsTemperatureBase>();
        }

        private void InitializePressureDescriptor()
        {
            m_pressureDescriptor = CreateScalarDescriptor<ILhsPressure>();
        }

        private void InitializeTemperatureDescriptor()
        {
            m_temperatureDescriptor = CreateScalarDescriptor<ILhsTemperature>();
        }

        private void InitializeMixtureFractionDescriptor()
        {
            m_mixtureFractionDescriptor = CreateScalarDescriptor<ILhsMixtureFraction>();
            if (m_mixtureFractionDescriptor != null && m_mixtureFractionDescriptor.IsQuestion) { throw new ArgumentException("mixture fraction must be defined"); }
        }

        private void InitializeEquivalenceRatioDescription()
        {
            m_equivalenceRatioDescriptor = CreateScalarDescriptor<ILhsEquivalenceRatio>();
            if (m_equivalenceRatioDescriptor != null && m_equivalenceRatioDescriptor.IsQuestion) { throw new ArgumentException("eq ratio must be defined"); }
        }

        private void InitializeFractionDescriptors()
        {
            var fractionLhs = SourceContract.LhsSet.OfType<ILhsCompositionBase>().ToList();
            m_fractionDescriptors = new FractionDescriptor[fractionLhs.Count];
            for (int ifr = 0; ifr < m_fractionDescriptors.Length; ++ifr)
            {
                ILhs lhs = fractionLhs[ifr];
                IRhs rhs = SourceContract[lhs];
                FractionDescriptor descriptor = new FractionDescriptor();
                if (lhs is ILhsSpecies)
                {
                    var sp = (ILhsSpecies)lhs;
                    descriptor.IsSpecies = true;
                    descriptor.Index = Expression.SpeciesIndex(sp.SpeciesId.ToLower());
                }
                else
                {
                    var cn = (ILhsComponent)lhs;
                    descriptor.IsSpecies = false;
                    descriptor.Index = Expression.ComponentIndex(cn.ComponentName.ToLower());
                }

                if (rhs is IRhsValue)
                {
                    double value = ((IRhsValue)rhs).Value;
                    descriptor.ExplicitValue = value;
                    if (value < 0.0) { throw new MixCompositionScaleViolationException(); }
                    if (this.CompositionScale.HasValue && value > this.CompositionScale.Value)
                    {
                        throw new MixCompositionScaleViolationException();
                    }
                }
                else if (rhs is IRhsEnumerable)
                {
                    IRhsEnumerable parameter = (IRhsEnumerable)rhs;
                    descriptor.ParameterIndex = Expression.ParameterIndex(parameter.ParameterName.ToLower());
                }
                m_fractionDescriptors[ifr] = descriptor;
            }

            if ( CompositionScale.HasValue )
            {
                if (m_fractionDescriptors.All(i => i.ExplicitValue.HasValue))
                {
                    double sum = m_fractionDescriptors.Select(i => i.ExplicitValue.Value).Sum();
                    var abstol = new DoubleAbsoluteTolerance(1.0e-8);
                    if (false == abstol.AreSame(sum, CompositionScale.Value))
                    {
                        throw new MixBadConditionedCompositionException();
                    }
                }

                int countImplicit = m_fractionDescriptors.Count(i => i.IsImplicit);

                
                if (countImplicit == 1 && m_fractionDescriptors.Count(i=>i.ParameterIndex.HasValue) == 0)
                {
                    double sum = m_fractionDescriptors.Where(i => i.ExplicitValue.HasValue).Sum(i => i.ExplicitValue.Value);
                    if (sum > CompositionScale.Value) { throw new MixBadConditionedCompositionException(); }
                }
            }
        }

        private ScalarDescriptor CreateScalarDescriptor<TLhs>() where TLhs : ILhs
        {
            var rhsBase =
                (from lhs in SourceContract.LhsSet.OfType<TLhs>()
                 select SourceContract[lhs]).SingleOrDefault();
            if (rhsBase == null) { return null; }

            var descriptor = new ScalarDescriptor();
            if (rhsBase is IRhsQuestion) { }
            else if (rhsBase is IRhsValue) { descriptor.ExplicitValue = ((IRhsValue)rhsBase).Value; }
            else if (rhsBase is IRhsEnumerable)
            {
                var rhs = (IRhsEnumerable)rhsBase;
                descriptor.ParameterIndex = Expression.ParameterIndex(rhs.ParameterName.ToLower());
            }
            else
            {
                throw new NotSupportedException();
            }
            return descriptor;
        }

        #endregion                

        #region Properties

        /// <summary>
        /// Component source contract
        /// </summary>
        internal IComponent SourceContract { get; private set; }

        internal ExpressionInfo Expression { get; private set; }

        internal int ComponentIndex { get; private set; }

        internal string ComponentName { get; private set; }

        internal string DisplayName { get; private set; }

        internal int FractionsCount { get { return m_fractionDescriptors.Length; } }

        internal bool IsRoot { get; private set; }

        internal bool IsMassBased { get; private set; }

        internal double? CompositionScale { get; private set; }

        internal ReadOnlyArray<ComponentInfo> SubComponents { get { return m_subComponents; } }

        internal ReadOnlyArray<string> PureSpecies { get { return m_speciesNames; } }

        internal TemperatureUnits TemperatureUnits { get; private set; }

        internal PressureUnits PressureUnits { get; private set; }

        internal bool IsFuOxCase
        {
            get 
            { 
                return 
                    (m_equivalenceRatioDescriptor != null && m_equivalenceRatioDescriptor.IsDefined) ||
                    (m_mixtureFractionDescriptor != null && m_mixtureFractionDescriptor.IsDefined);
            }
        }

        #endregion

        #region Internal Methods

        internal void FixRelations()
        {
            this.ComponentIndex = Expression.ComponentIndex(this.ComponentName);
            m_subComponents =
                (
                    from dtr in m_fractionDescriptors
                    where dtr.IsComponent
                    select Expression.Components[dtr.Index]
                ).ToArray();
            m_speciesNames =
                (
                    from dtr in m_fractionDescriptors
                    where dtr.IsSpecies
                    select Expression.SpeciesIDs[dtr.Index]
                ).ToArray();
        }

        internal void Fill(MixProjection projection)
        {
            m_calcCompositionHandler(projection);
            m_tpHandler(projection);
        } 

        #endregion

        #region Handlers evaluating the temperature and the pressure

        private void BasicTpCalculator(MixProjection projection)
        {
            var component = projection.CompositionObjects[this.ComponentIndex];
            component.Temperature = this.DetermineTemperature(projection);
            component.Pressure = this.DeterminePressure(projection);            
        }

        private void AdiabaticMixingTpCalculator(MixProjection projection)
        {
            Debug.Assert(m_pressureDescriptor.IsDefined, "the pressure must defined either implicitely or as a parameter");
            Debug.Assert(m_temperatureDescriptor.IsQuestion, "the temperature must be defined as an implicit/[?] value");
            Debug.Assert(m_baseTemperatureDescriptor == null, "the base temperature must not be defined");
            Debug.Assert(m_basePressureDescriptor == null, "the base pressure must not be defined");

            if (m_pressureDescriptor.IsDefined == false ||
                m_temperatureDescriptor.IsQuestion == false ||
                m_baseTemperatureDescriptor != null ||
                m_basePressureDescriptor != null)
            {
                throw new InvalidOperationException();
            }
            
            var thisComposition = projection.CompositionObjects[this.ComponentIndex];
            Pressure pressure = this.DeterminePressure(projection);
            thisComposition.ApplyAdiabaticMixing(pressure);
        }

        private void ShockWaveTpCalculator(MixProjection projection)
        {
            var thisComposition = projection.CompositionObjects[this.ComponentIndex];
            thisComposition.BasePressure = DetermineBasePressure(projection);
            if (m_baseTemperatureDescriptor.IsDefined)
            {
                thisComposition.BaseTemperature = DetermineBaseTemperature(projection);
            }
            else
            {
                // the base temperature need to be determined based on the component fractions
                thisComposition.ApplyAdiabaticMixing(thisComposition.BasePressure.Value);
                thisComposition.BaseTemperature = thisComposition.Temperature;
            }

            if (m_compressionRatioDescriptor != null)
            {
                thisComposition.CompressionRatio = DetermineCompressionRatio(projection);
            }
            else if (m_machNumberDescriptor != null && m_machNumberDescriptor.IsDefined)
            {
                thisComposition.MachNumber = DetermineMachNumber(projection);
            }
            Temperature? temperature = null; 
            Pressure? pressure = null;
            if (m_temperatureDescriptor != null && m_temperatureDescriptor.IsDefined)
            {
                temperature = DetermineTemperature(projection);
            }
            if (m_pressureDescriptor != null && m_pressureDescriptor.IsDefined)
            {
                pressure = DeterminePressure(projection);
            }
            thisComposition.ApplyShockWaveCompression(temperature, pressure);

        }

        private void IsentropicCompressionTpCalculator(MixProjection projection)
        {
            if (m_pressureDescriptor == null || m_temperatureDescriptor == null ||
                m_basePressureDescriptor == null || m_baseTemperatureDescriptor == null) { throw new InvalidOperationException(); }

            if (m_compressionRatioDescriptor != null && m_compressionRatioDescriptor.IsDefined)
            {
                if (m_temperatureDescriptor.IsQuestion == false || m_pressureDescriptor.IsQuestion == false) { throw new InvalidOperationException(); }
            }
            else if (m_compressionRatioDescriptor == null)
            {
                if (m_temperatureDescriptor.IsDefined == m_pressureDescriptor.IsDefined) { throw new InvalidOperationException(); }
            }


            var thisComposition = projection.CompositionObjects[this.ComponentIndex];
            thisComposition.BasePressure = DetermineBasePressure(projection);
            if (m_baseTemperatureDescriptor.IsDefined)
            {
                thisComposition.BaseTemperature = DetermineBaseTemperature(projection);
            }
            else
            {
                // the base temperature need to be determined based on the component fractions
                thisComposition.ApplyAdiabaticMixing(thisComposition.BasePressure.Value);
                thisComposition.BaseTemperature = thisComposition.Temperature;
            }

            if (m_temperatureDescriptor.IsQuestion && m_pressureDescriptor.IsDefined)
            {       
                Pressure pressure = DeterminePressure(projection);
                thisComposition.ApplyIsentropicCompression(pressure);                
            }
            else if( m_pressureDescriptor.IsQuestion && m_temperatureDescriptor.IsDefined)
            {
                Temperature temperature = DetermineTemperature(projection);
                thisComposition.ApplyIsentropicCompression(temperature);                                
            }
            else if (m_pressureDescriptor.IsQuestion && m_temperatureDescriptor.IsQuestion && m_compressionRatioDescriptor.IsDefined)
            {
                CompressionRatio compressionRatio = DetermineCompressionRatio(projection);
                thisComposition.ApplyIsentropicCompression(compressionRatio);                
            }
        }

        private CompressionRatio DetermineCompressionRatio(MixProjection projection)
        {
            double? c = GetValue(m_compressionRatioDescriptor, projection);
            if (c.HasValue) { return new CompressionRatio(c.Value); }
            throw new InvalidOperationException();            
        }

        private MachNumber DetermineMachNumber(MixProjection projection)
        {
            double? m = GetValue(m_machNumberDescriptor, projection);
            if (m.HasValue) { return new MachNumber(m.Value); }
            throw new InvalidOperationException();
        }


        private Temperature DetermineTemperature(MixProjection projection)
        {
            double? t = GetValue(m_temperatureDescriptor, projection);
            if (t.HasValue) { return new Temperature(t.Value, this.TemperatureUnits); }
            else if (Expression.SetupInfo.DefaultTemperature.HasValue) { return Expression.SetupInfo.DefaultTemperature.Value; }
            throw new InvalidOperationException();
        }

        private Pressure DeterminePressure(MixProjection projection)
        {
            double? p = GetValue(m_pressureDescriptor, projection);
            if (p.HasValue) { return new Pressure(p.Value, this.PressureUnits); }
            else if (Expression.SetupInfo.DefaultPressure.HasValue) { return Expression.SetupInfo.DefaultPressure.Value; }
            throw new InvalidOperationException();
        }

        private Temperature DetermineBaseTemperature(MixProjection projection)
        {
            double? t = GetValue(m_baseTemperatureDescriptor, projection);
            if (t.HasValue) { return new Temperature(t.Value, this.TemperatureUnits); }
            throw new InvalidOperationException();
        }        

        private Pressure DetermineBasePressure(MixProjection projection)
        {
            double? p = GetValue(m_basePressureDescriptor, projection);
            if (p.HasValue) { return new Pressure(p.Value, this.PressureUnits); }
            throw new InvalidOperationException();
        }

        private double? GetValue(ScalarDescriptor descriptor, MixProjection projection)
        {
            if (descriptor != null)
            {
                if (descriptor.ExplicitValue.HasValue) { return descriptor.ExplicitValue; }
                else if (descriptor.ParameterIndex.HasValue)
                {
                    double value = projection.GetParameterValue(descriptor.ParameterIndex.Value);
                    return value;
                }
                else { throw new InvalidOperationException(); }
            }
            return null;
        }

        #endregion

        #region Handlers avaluating chemical composition 

        private void BasicCompositionCalculator(MixProjection projection)
        {
            double[] fractions = new double[FractionsCount];
            CompositionObject[] compositions = new CompositionObject[FractionsCount];

            int? implicitIndex = null;
            double sum = 0.0;
            for (int ifr = 0; ifr < m_fractionDescriptors.Length; ++ifr)
            {
                var desc = m_fractionDescriptors[ifr];
                compositions[ifr] = desc.IsSpecies
                    ? Expression.SpeciesCompositions[desc.Index]
                    : projection.CompositionObjects[desc.Index];

                if (desc.ExplicitValue != null)
                {
                    double value = desc.ExplicitValue.Value;
                    fractions[ifr] = value;
                    sum += value;
                }
                else if (desc.ParameterIndex != null)
                {
                    double value = projection.GetParameterValue(desc.ParameterIndex.Value);
                    fractions[ifr] = value;
                    sum += value;
                }
                else
                {
                    implicitIndex = ifr;
                }
            }



            if (implicitIndex != null)
            {
                fractions[implicitIndex.Value] = CompositionScale.Value - sum;
                if (fractions[implicitIndex.Value] < 0.0)
                {
                    throw new MixCompositionScaleViolationException();
                }
                sum = CompositionScale.Value;
            }

            if (sum != 1.0)
            {
                for (int ifr = 0; ifr < FractionsCount; ++ifr) { fractions[ifr] /= sum; }
            }

            CompositionObject comp = new CompositionObject(this);
            comp.SetFractions(compositions);


            if (IsMassBased)
            {
                double[] y = new double[Expression.SpeciesIDs.Length];
                for (int ifr = 0; ifr < FractionsCount; ++ifr)
                {
                    double coeff = fractions[ifr];
                    compositions[ifr].AddToY(y, coeff);
                }
                comp.Y = y;
                comp.SetFractionsY(fractions);
            }
            else
            {
                double[] x = new double[Expression.SpeciesIDs.Length];
                for (int ifr = 0; ifr < FractionsCount; ++ifr)
                {
                    double coeff = fractions[ifr];
                    compositions[ifr].AddToX(x, coeff);
                }
                comp.X = x;
                comp.SetFractionsX(fractions);
            }


            projection.Add(comp);
        }

        private void EquivalnceRatioCompositionCalculator(MixProjection projection)
        {
            ScalarDescriptor descriptor = m_equivalenceRatioDescriptor;

            double[] fractions = new double[2];
            CompositionObject[] compositions = new CompositionObject[2];
            int indexFuel = 0;
            int indexOxidizer = 1;

            for (int ifr = 0; ifr < m_fractionDescriptors.Length; ++ifr)
            {
                var desc = m_fractionDescriptors[ifr];
                compositions[ifr] = desc.IsSpecies
                    ? Expression.SpeciesCompositions[desc.Index]
                    : projection.CompositionObjects[desc.Index];

                if (desc.IsImplicit == false) { throw new InvalidOperationException(); }
            }

            if (compositions[0].CHOFormula.IsFuel && compositions[1].CHOFormula.IsOxydizer) { }
            else if (compositions[0].CHOFormula.IsOxydizer && compositions[1].CHOFormula.IsFuel)
            {
                indexFuel = 1;
                indexOxidizer = 0;
            }
            else
            {
                throw new InvalidOperationException();
            }

            EquivalenceRatio ratio;
            ratio = new EquivalenceRatio(
                        (descriptor.ExplicitValue != null
                        ? descriptor.ExplicitValue.Value
                        : projection.GetParameterValue(descriptor.ParameterIndex.Value))
                    );
            if (false == ratio.IsValid)
            {
                throw new ArgumentOutOfRangeException();
            }

            double xfu = CHOFormula.CalcFuelMoleFraction(
                compositions[indexFuel].CHOFormula.OxygenBalance,
                compositions[indexOxidizer].CHOFormula.OxygenBalance,
                ratio);

            fractions[indexFuel] = xfu;
            fractions[indexOxidizer] = (1.0 - xfu);

            double[] x = new double[Expression.Nsp];

            compositions[indexFuel].AddToX(x, xfu);
            compositions[indexOxidizer].AddToX(x, 1.0 - xfu);

            CompositionObject comp = new CompositionObject(this);
            comp.SetFractions(compositions);
            comp.X = x;
            comp.SetFractionsX(fractions);
            comp.EquivalenceRatio = ratio;
            comp.MixtureFraction = new MixtureFraction(comp.YComp[indexFuel]);
            projection.Add(comp);

        }

        private void MixtureFractionCompositionCalculator(MixProjection projection)
        {
            ScalarDescriptor descriptor = m_mixtureFractionDescriptor;

            double[] fractions = new double[2];
            CompositionObject[] compositions = new CompositionObject[2];
            int indexFuel = 0;
            int indexOxidizer = 1;

            for (int ifr = 0; ifr < m_fractionDescriptors.Length; ++ifr)
            {
                var desc = m_fractionDescriptors[ifr];
                compositions[ifr] = desc.IsSpecies
                    ? Expression.SpeciesCompositions[desc.Index]
                    : projection.CompositionObjects[desc.Index];

                if (desc.IsImplicit == false) { throw new InvalidOperationException(); }
            }

            if (compositions[0].CHOFormula.IsFuel && compositions[1].CHOFormula.IsOxydizer) { }
            else if (compositions[0].CHOFormula.IsOxydizer && compositions[1].CHOFormula.IsFuel)
            {
                indexFuel = 1;
                indexOxidizer = 0;
            }
            else
            {
                throw new InvalidOperationException();
            }

            MixtureFraction mixFrac;
            mixFrac = new MixtureFraction(
                        (descriptor.ExplicitValue != null
                        ? descriptor.ExplicitValue.Value
                        : projection.GetParameterValue(descriptor.ParameterIndex.Value))
                    );

            fractions[indexFuel] = mixFrac;
            fractions[indexOxidizer] = (1.0 - mixFrac);

            double[] y = new double[Expression.Nsp];

            compositions[indexFuel].AddToY(y, mixFrac);
            compositions[indexOxidizer].AddToY(y, 1.0 - mixFrac);

            CompositionObject comp = new CompositionObject(this);
            comp.SetFractions(compositions);
            comp.Y = y;
            comp.SetFractionsY(fractions);
            comp.MixtureFraction = mixFrac;
            comp.EquivalenceRatio = new EquivalenceRatio(
                    CHOFormula.CalcEquivalenceRatio(
                    compositions[indexFuel].CHOFormula,
                    compositions[indexOxidizer].CHOFormula,
                    comp.XComp[indexFuel])
                );
            projection.Add(comp);

        } 

        #endregion
        
    }
}
